#define  _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <errno.h>
#include <syscall.h>

#include "conn.h"
#include "../config/config.h"



int pidfd_open(pid_t pid,unsigned int flags){
    return syscall(SYS_pidfd_open, pid, flags);
}

int pidfd_getfd(int pidfd,int targetfd,unsigned int flags){
    return syscall(SYS_pidfd_getfd,pidfd,targetfd,flags);
}



//设置子进程中关闭父进程继承的fd 
int fdcloexec(int fd){
    int old_flags=fcntl(fd,F_GETFD);
    if (old_flags==-1)
    {
        return -1;
    }
    int new_flags=old_flags|FD_CLOEXEC;
    if (fcntl(fd,F_SETFD,new_flags)==-1)
    {
        return -1;
    }
    return 0;
}

int fdnonblock(int fd){
    int old_flags=fcntl(fd,F_GETFL);
    if (old_flags==-1)
    {
        return -1;
    }
    int new_flags=old_flags|O_NONBLOCK;
    if (fcntl(fd,F_SETFL,new_flags)==-1)
    {
        return -1;
    }
    return 0;
}

//判断fd类型 1.dir 2.file 3.link 4.char 5.blk 6.fifo 7.sock
int fdtype(int fd){
    struct stat info;
    int ret=fstat(fd,&info);
    if (ret==-1)
    {
        return -1;
    }
    if (S_ISDIR(info.st_mode))
    {
        return 1;
    }else if (S_ISREG(info.st_mode))
    {
        return 2;
    }else if (S_ISLNK(info.st_mode))
    {
        return 3;
    }else if (S_ISCHR(info.st_mode))
    {
        return 4;
    }else if (S_ISBLK(info.st_mode))
    {
        return 5;
    }else if (S_ISFIFO(info.st_mode))
    {
        return 6;
    }else if (S_ISSOCK(info.st_mode))
    {
        return 7;
    }
    return 0; 
}

/**
 <<epoll事件控制>>
将带有fd的epoll msg注册到epollfd (epmsg需要malloc)
**/
void epollset(int epollfd,int action,epmsg_t *epmsg){
    struct epoll_event ev;
    ev.events = epmsg->events;
    ev.data.ptr=epmsg; 
    epoll_ctl(epollfd,action,epmsg->fd,&ev);
    if (action==EPOLL_CTL_DEL && epmsg != NULL)
    {
        free(epmsg);
    }
}


epmsg_t *epmsg_init(int fd,int evts,FILE* stream,void *extend){
    epmsg_t *epmsg=calloc(1,sizeof(epmsg_t));
    epmsg->events=evts;
    epmsg->extend=extend;
    epmsg->fd=fd;
    epmsg->stream=stream;
    return epmsg;
}

int epmsg_add(int epollfd,epmsg_t *epmsg){
    struct epoll_event ev;
    ev.events=epmsg->events;
    ev.data.ptr=epmsg;
    int ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,epmsg->fd,&ev);
    return ret;
}

int epmsg_del(int epollfd,epmsg_t *epmsg){
    int ret=epoll_ctl(epollfd,EPOLL_CTL_DEL,epmsg->fd,NULL);
    if (ret!=-1){
        if (epmsg->stream!=NULL)
        {
            fclose(epmsg->stream);
        }else{
            close(epmsg->fd);
        }
        free(epmsg);
    }
    return ret;
}


/**
 *从fd中按行读取数据:
 * 1. FILE *file_stream=fdopen(fd,"r");
 * 2. setlinebuf(file_stream);
 * 3. char line[4096];
 * 4. int linemax=sizeof(line);
 * 5. fgets(line,linemax, file_stream);
 **/
void freadlines(void *user,FILE *__in,FILE *__out,unsigned int iosize,io_work __worker){
    if (iosize<1024)
        iosize=1024;
    char buf[iosize];
    while (fgets(buf,iosize,__in))
    {   
        __worker(user,buf,__out);    
    }
}

//发送fd: 如果send_fd=-1只发数据,成功0,失败-1.
ssize_t sendfd(int sock_fd, int send_fd,const void *messages,unsigned int msglen)
{
    if (send_fd<0 && messages==NULL)
        return -1;
    //发送普通数据
    struct msghdr msg;
    struct cmsghdr *p_cmsg;
    struct iovec vec;
    char cmsgbuf[CMSG_SPACE(sizeof(send_fd))];
    int *p_fds;
    msg.msg_control = cmsgbuf;
    msg.msg_controllen = sizeof(cmsgbuf);
    p_cmsg = CMSG_FIRSTHDR(&msg);
    p_cmsg->cmsg_level = SOL_SOCKET;
    p_cmsg->cmsg_type = SCM_RIGHTS;
    p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
    p_fds = (int *)CMSG_DATA(p_cmsg);
    *p_fds = send_fd; // 通过传递辅助数据的方式传递文件描述符
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = &vec;
    msg.msg_iovlen = 1; //主要目的不是传递数据，故只传1个
    msg.msg_flags = 0;
    vec.iov_base = (void*)messages;
    vec.iov_len = msglen; 
    //发送
    size_t ret = sendmsg(sock_fd, &msg, 0);
    if (ret < 0)
        printf("sendmsg err :%s\n",strerror(errno));
    return ret; 
}

//接收fd: 没辅助数据则recv_fd=-1.成功返回数据长度,失败-1。
ssize_t recvfd(int sock_fd,int *recv_fd,void *messages,unsigned int msglen)
{
    if (recv_fd==NULL && messages==NULL)
        return -1;
    struct msghdr msg;
    memset(&msg,0,sizeof(msg));

    //设置普通数据接收buf
    struct iovec vec;
    memset(&vec,0,sizeof(vec));
    if (messages!=NULL)
    {
        vec.iov_base = messages;
        vec.iov_len = msglen;  
    }else{
        vec.iov_base = NULL;
        vec.iov_len = 0;
    }
    
    char cmsgbuf[CMSG_SPACE(4)];
    struct cmsghdr *p_cmsg=NULL;

    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = &vec;
    msg.msg_iovlen = 1;
    msg.msg_control = cmsgbuf;
    msg.msg_controllen = sizeof(cmsgbuf);
    msg.msg_flags = MSG_CMSG_CLOEXEC;
    
    int *p_fd = (int *)CMSG_DATA(CMSG_FIRSTHDR(&msg));
    *p_fd = -1;
    int ret = recvmsg(sock_fd, &msg,MSG_TRUNC|MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
    if (ret != -1 && recv_fd != NULL) //接收到数据
    {
        p_cmsg = CMSG_FIRSTHDR(&msg);
    }
    if (p_cmsg == NULL)             //没接收到fd
    {
        *recv_fd=-1;
        printf("no passed fd 8\n");
    }
    p_fd = (int *)CMSG_DATA(p_cmsg);
    *recv_fd = *p_fd;
    if (*recv_fd == -1)              //没接收到fd
    {
        printf("no passed fd\n");
    }
    return ret;                     //返回接收的数据
}

//监听socket: 成功返回fd，失败返回-1
int listen_unix(const char *sock_path,int type,int listen_queue){
    char dirpath[128];
    char filename[128];
    split_dir_file(dirpath,filename,sock_path);
    trim(dirpath);
    trim(filename);
    if (access(dirpath,F_OK)==0)
    {
        if (access(sock_path,F_OK)==0)
        {
            unlink(sock_path);
        }
        
    }else{
        mkdir(dirpath,0755);
    }
    int server_socket;
    struct sockaddr_un server_addr;

    //创建套接字
    server_socket = socket(AF_UNIX, type, 0);
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, sock_path);
    int slen = sizeof(server_addr);
    //绑定套接字
    bind(server_socket, (struct sockaddr *) &server_addr, slen);
    //监听套接字,接收队列的长度5，在Server程序调用accept函数之前最大允许进入的连接请求数，多余的连接请求将被拒绝，典型取值为5
    listen(server_socket,listen_queue);
    return server_socket;
}

//连接socket: 成功返回fd,失败返回-1
int connect_unix(const char *sock_path,int type){
    int server_socket;
    struct sockaddr_un server_addr;
    server_socket = socket(AF_UNIX,type, 0);
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, sock_path);
    if (connect(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))==0)
    {
        return server_socket;
    }else{
        return -1;
    }
}

//转发fd
int fdproxy(int src_sock,int dst_sock){
    int proxyfd;
    char buf[1024];
    int ret=recvfd(src_sock,&proxyfd,buf,sizeof(buf));
    if (ret==-1||proxyfd==-1)
    {
        return -1;
    }
    ret=sendfd(dst_sock,proxyfd,buf,ret);
    if (ret==-1)
    {
        return -1;
    }
    return 0;
}



int fdhandle(const char *sockpath,const char *msg){
    // 连接fd server
    int client_fd;
    if ((client_fd=connect_unix(sockpath,SOCK_STREAM|SOCK_CLOEXEC))<0) {
        perror("fdhandle:");
        return -1;
    }

    // 向服务器注册remote_fd
    int sock_peer[2];
    if (socketpair(AF_UNIX,SOCK_STREAM|SOCK_CLOEXEC,0,sock_peer)==0)
    {
        // 客户注册remote_fd
        int msglen=0;
        if (msg!=NULL)
        {
            msglen=strlen(msg)+1;
        }
        ssize_t ret=sendfd(client_fd,sock_peer[1],msg,msglen);
        if (ret>0)
        {
            close(sock_peer[1]);
            return sock_peer[0];
        }else{
            close(sock_peer[1]);
            close(sock_peer[0]);
            printf("sendmsg: %s\n",strerror(errno));
            return -1;
        }
    }
    close(client_fd);
    return -1;
}

//request fd from server
int fdrequest(const char *_path,int *recv_fd,void *msg,unsigned int msglen){
    int connet_fd=connect_unix(_path,SOCK_CLOEXEC|SOCK_STREAM|SOCK_NONBLOCK);
    if (connet_fd==-1)
    {
        return -1;
    }
    CONN_t handle_msg;
    memset(&handle_msg,0,sizeof(handle_msg));
    handle_msg.header.magic=CONN_MAGIC_FDMSG;
    handle_msg.header.action=CONN_FD_REQUEST;
    handle_msg.header.payloadlen=msglen;
    send(connet_fd,&handle_msg,sizeof(handle_msg.header)+msglen,0);
    int ret=recvfd(connet_fd,recv_fd,NULL,0);
    close(connet_fd);
    return ret;
}

int inotify(const char *path,void *info,ssize_t n){
    int client_fd=connect_unix(path,SOCK_STREAM|SOCK_NONBLOCK);
    if (client_fd<0)
    {
        return -1;
    }
    int ret=send(client_fd,info,n,SOCK_NONBLOCK);
    close(client_fd);
    return ret;  
}

